home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
By Popular Request 2.0
/
By Popular Request 2.0 (Arsenal Computer).ISO
/
amiga_6
/
wbtitl14.lha
/
WBTitle
/
Source
/
WBTitle.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-26
|
16KB
|
591 lines
/*
* WBTitle
*
* Version 1.4
*
* v1.0 Original public release by Mark Thomas
*
* v1.3d German locale version by Martin Kaim (Gwardar), fixes/features:
* - removed a bug in the numeric display routine (v1.0 would print e.g. 10,274,380 as
* :,274,380 )
* - now ToolType SEPARATOR is spelled correctly (instead of SEPERATOR),
* also the default separator for German locale now is '.' instead of ','
* - supplied smakefile
* - version now works with the German locale (ONLY) (different Workbench title bar)
* (can/must be adjusted to any other locale by slight changes in the source and
* recompiling)
* - supplied Icon with a nice tooltype setting (in German only)
* - adjusted documentation to reflect these changes
*
* v1.4 Multi-locale update by Gwardar, changes/features:
*
* This version *finally* supports ALL locales by using two new ToolType entries!
*
* - Switched back to English locale as *default* configuration (convenient for the
* English-speaking Amiga community :-)
* - You can now configure this program to *whatever* your original Workbench title
* might read, so WBTitle is *no longer* limited to English- or German-speaking users
* :-)
* - Supplied 2 icons with appropriate tooltype entries for English and German locales.
* - Now compiled with SAS/C v6.55 (which increases the excutable size by ~120 bytes)
* but nevertheless the patch is worth it.
*
* - Adjusted docs to reflect changes.
*
* - NB: allows for up to 2^32 - 1 bytes (~4 GB) of memory (who knows, in a few
* years from now this *might* be the standard virtual memory amount on Amigas) ;->
*
* Public Domain Software
*
* This program replaces the Amiga's Workbench title bar so that it shows the amount
* of all types of FREE memory currently available: Chip, Fast, Public;
* further VMM (if installed and ACTIVE), and Retina Gfx board memory (if applicable).
*/
#include <string.h>
#include <ctype.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/retina.h>
// Gwardar begin
//#include <proto/vmm.h> // Gwardar: if you don't have the proto file,
// so what? It's not really needed.
#include <pragmas/retina_pragmas.h> // Gwardar: you may comment this out, if
// you don't have the Retina stuff
// Gwardar end
#include <clib/alib_protos.h>
// The SetWindowTitles function offset
#define SWTOffset -276
enum {
JMPINSTR = 0x4ef9
};
typedef struct JmpEntry {
UWORD Instr;
APTR Func;
} JmpEntry;
static BOOL Replace(void);
static void Restore(void);
static void __asm new_SetWindowTitles(register __a0 struct Window *,
register __a1 UBYTE *, register __a2 UBYTE *,
register __a6 struct Library *);
static void NumberToString(ULONG number);
static void format_number(ULONG number);
static void no_lead_triple(ULONG number);
static void lead_triple(ULONG number);
// Gwardar begin
//ULONG AvailVMem(ULONG);
ULONG Retina_AvailMem(ULONG);
// Gwardar end
// Global variables
struct RetinaBase *RetinaBase;
struct Library *VMMBase;
// Local variables
static char verstring[] = "$VER: WBTitle 1.4 (multi-locale version by Gwardar) " __AMIGADATE__;
static char port_name[] = "WBTitle";
static void __asm (*old_SetWindowTitles)(register __a0 struct Window *,
register __a1 UBYTE *,
register __a2 UBYTE *,
register __a6 struct Library *);
static JmpEntry *SWTEntry;
// Gwardar begin
static char *match1_str, *match2_str;
// Gwardar end
static char *chip_str, *fast_str, *public_str, *total_str, *virtual_str;
static char *retina_str, *memorder_str, *prefix_str, *suffix_str, *labels_str;
static char *comma_str, *mem_str, *last_str, *buff_ptr;
static char num_buf[16], comma;
static int units, num_size, mem_str_len, prefix_str_len;
static BOOL sep1000, labels_after;
/*
* The main method for replacing an Amiga OS function as safe as
* possible is to place the function with a jump table that is
* allocated. While the function is replaced, the jump table simply
* jumps to my routine:
*
* jmp _new_SetWindowTitles
*
* When the user asks the program to quit, we can't simply put the
* pointer back that SetFunction() gives us since someone else might
* have replaced the function. So, we first see if the pointer we
* get back points to the out jump table. If so, then we _can_ put
* the pointer back like normal (no one has replaced the function
* while we has it replaced). But if the pointer isn't mine, then
* we have to replace the jump table function pointer to the old
* function pointer:
*
* jmp _old_SetWindowTitles
*
* Finally, we only deallocate the jump table _if_ we did not have
* to change the jump table.
*/
main(int argc, char *argv[])
{
struct MsgPort *port;
// FindPort() Forbid()
Forbid();
port = FindPort(port_name);
if (port) {
struct MsgPort *reply_port;
// Create a reply port
reply_port = CreateMsgPort();
if (reply_port) {
struct Message msg;
// Set fields in message structure
msg.mn_ReplyPort = reply_port;
msg.mn_Length = sizeof(struct Message);
// Send the message
PutMsg(port, &msg);
// Finished with port, so stop FindPort() Forbid()
Permit();
// Wait for a reply
do {
WaitPort(reply_port);
} while (GetMsg(reply_port) == NULL);
// Clear and Delete reply_port Forbid()
Forbid();
// Clear any messages
while (GetMsg(reply_port));
// Delete the reply port
DeleteMsgPort(reply_port);
// Clear and Delete reply_port stop Forbid()
Permit();
} else {
// Finished with port, so stop FindPort() Forbid()
Permit();
}
} else if (port = CreateMsgPort()) {
struct Message *msg;
char **ttypes;
// Finished with port, so stop FindPort() Forbid()
Permit();
// Setup quitting port
port->mp_Node.ln_Name = port_name;
port->mp_Node.ln_Pri = -120;
// Add quitting port to public list
AddPort(port);
// Open the Retina library
RetinaBase = (struct RetinaBase *)OpenLibrary("retina.library", 0);
// Check on VMM (only if ACTIVE!): look for port, and open lib
if (FindPort("VMM_Port")) {
VMMBase = OpenLibrary("vmm.library", 0);
}
// Setup to read some arguments
ttypes = ArgArrayInit(argc, argv);
// Read some arguments
// Gwardar begin
// these two new tooltypes allow for the multi-locale support
// by enabling you to change the matching strings via tooltype entries
match1_str = ArgString(ttypes, "MATCH1", "Amiga"); //default match 1 = "Amiga"
match2_str = ArgString(ttypes, "MATCH2", "graphics mem"); //default match 2 = "graphics mem"
// Gwardar end
prefix_str = ArgString(ttypes, "PREFIX", ""); // Gwardar: default = ""
suffix_str = ArgString(ttypes, "SUFFIX", "");
labels_str = ArgString(ttypes, "LABELS", "AFTER");
chip_str = ArgString(ttypes, "CHIP", " Chip ");
fast_str = ArgString(ttypes, "FAST", " Fast ");
public_str = ArgString(ttypes, "PUBLIC", " Public ");
total_str = ArgString(ttypes, "TOTAL", " Total ");
virtual_str = ArgString(ttypes, "VIRTUAL", " Virtual ");
retina_str = ArgString(ttypes, "RETINA", " Retina ");
memorder_str = ArgString(ttypes, "MEMORDER", "CVPR");
sep1000 = (ArgString(ttypes, "THOUSANDSEP", NULL) != NULL);
units = ArgInt(ttypes, "UNITS", 1);
comma_str = ArgString(ttypes, "SEPARATOR", NULL);
if (comma_str && comma_str[0]) {
comma = comma_str[0];
} else {
comma = ','; // separator defaults to ',' for English locale
}
labels_after = (stricmp(labels_str, "BEFORE") != 0);
if (units < 0) {
units = 1;
}
// Compute the size of numbers
{
ULONG maxnum = 0xffffffff;
// Compute the number of digits
maxnum /= units;
while (maxnum) {
maxnum /= 10;
num_size++;
}
// Compute the number of commas
if (sep1000) {
int com = num_size;
while (com > 3) {
num_size++;
com -= 3;
}
}
}
// Compute the size of the mem string
{
ULONG i = 0;
char ch;
// Add in the prefix string size
mem_str_len = prefix_str_len = strlen(prefix_str);
// Add in the memory parts
while (ch = toupper(memorder_str[i])) {
switch (ch) {
case 'C':
mem_str_len += strlen(chip_str) + num_size;
break;
case 'F':
mem_str_len += strlen(fast_str) + num_size;
break;
case 'P':
mem_str_len += strlen(public_str) + num_size;
break;
case 'T':
mem_str_len += strlen(total_str) + num_size;
break;
case 'V':
if (VMMBase) {
mem_str_len += strlen(virtual_str) + num_size;
}
break;
case 'R':
if (RetinaBase) {
mem_str_len += strlen(retina_str) + num_size;
}
break;
}
i++;
}
// Add in the suffix string size
mem_str_len += strlen(suffix_str);
}
if (mem_str_len > 0) {
// Allocate the memory
mem_str = AllocMem(mem_str_len + 1, MEMF_PUBLIC);
if (mem_str) {
strcpy(mem_str, prefix_str);
last_str = &mem_str[prefix_str_len];
// Attempt to replace function
if (Replace()) {
// Wait for someone to signal me to quit
// e.g. WBTitle started again
do {
WaitPort(port);
msg = GetMsg(port);
} while (msg == NULL);
ReplyMsg(msg);
// Restore function
Restore();
}
FreeMem(mem_str, mem_str_len + 1);
}
}
// Cleanup from reading arguments
ArgArrayDone();
// Remove port from public access
RemPort(port);
// Clear and Delete port Forbid()
Forbid();
// Clear the port of messages
while (msg = GetMsg(port)) {
ReplyMsg(msg);
}
// Closedown quitting port
DeleteMsgPort(port);
// Clear and Delete port stop Forbid()
Permit();
}
}
static BOOL Replace(void)
{
// Allocate the jump table
SWTEntry = AllocMem(sizeof(JmpEntry), 0);
if (SWTEntry) {
// Replacement Forbid()
Forbid();
// Replace the function with pointer to jump table
old_SetWindowTitles = SetFunction((struct Library *)IntuitionBase,
SWTOffset, (ULONG (*)())SWTEntry);
// Setup the jump table
SWTEntry->Instr = JMPINSTR;
SWTEntry->Func = new_SetWindowTitles;
// Clear the CPU's cache so the execution cache is valid
CacheClearU();
// Stop the replacement Forbid()
Permit();
return TRUE;
} else {
return FALSE;
}
}
static void Restore(void)
{
BOOL my_table;
ULONG (*func)();
// Fix back Forbid()
Forbid();
// Put old pointer back and get current pointer at same time
func = SetFunction((struct Library *)IntuitionBase, SWTOffset,
(ULONG (*)())old_SetWindowTitles);
// Check to see if the pointer we get back is ours
if ((JmpEntry *)func != SWTEntry) {
// If not, leave jump table in place
my_table = FALSE;
SetFunction((struct Library *)IntuitionBase, SWTOffset,
func);
SWTEntry->Func = old_SetWindowTitles;
} else {
// If so, free the jump table
my_table = TRUE;
FreeMem(SWTEntry, sizeof(JmpEntry));
}
// Clear the CPU's cache so the execution cache is valid
CacheClearU();
// Stop fix back Forbid()
Permit();
// Let the user know if the jump table couldn't be freed
if (!my_table) {
DisplayBeep(NULL);
}
// Wait 5 seconds to try and guarantee that all tasks have
// finished executing inside my replacement function before
// quitting. There's no real way to guarantee, though.
Delay(250);
}
static void __saveds __asm new_SetWindowTitles(
register __a0 struct Window *window,
register __a1 UBYTE *win_title,
register __a2 UBYTE *scr_title,
register __a6 struct Library *lib)
{
// try to make sure only the Workbench title bar gets fixed
// Gwardar begin
// compares the title bar to contain both the string specified by
// tooltype MATCH1 (defaults to: "Amiga") and the string specified by
// tooltype MATCH2 (defaults to: "graphics mem") and may thus be adjusted
// to ANY locale/Workbench environment *without* the need to change the
// source and recompile.
// the default matches '#??Amiga (#??.)#??.??? graphics mem#??'
// (contained in the original Workbench's title bar in English locale)
// Gwardar end
if (scr_title && (scr_title != (UBYTE *)0xffffffff) &&
strstr(scr_title, match1_str) && strstr(scr_title, match2_str)) {
ULONG i = 0;
char ch;
// Set last_str to nothing
*last_str = 0;
// Add in the memory parts
while (ch = toupper(memorder_str[i])) {
switch (ch) {
case 'C':
if (!labels_after) {
strcat(last_str, chip_str);
}
NumberToString(AvailMem(MEMF_CHIP));
strcat(last_str, num_buf);
if (labels_after) {
strcat(last_str, chip_str);
}
break;
case 'F':
if (!labels_after) {
strcat(last_str, fast_str);
}
NumberToString(AvailMem(MEMF_FAST));
strcat(last_str, num_buf);
if (labels_after) {
strcat(last_str, fast_str);
}
break;
case 'P':
if (!labels_after) {
strcat(last_str, public_str);
}
NumberToString(AvailMem(MEMF_PUBLIC));
strcat(last_str, num_buf);
if (labels_after) {
strcat(last_str, public_str);
}
break;
case 'T':
if (!labels_after) {
strcat(last_str, total_str);
}
NumberToString(AvailMem(0));
strcat(last_str, num_buf);
if (labels_after) {
strcat(last_str, total_str);
}
break;
case 'V':
if (VMMBase) {
if (!labels_after) {
strcat(last_str, virtual_str);
}
NumberToString(AvailVMem(0));
strcat(last_str, num_buf);
if (labels_after) {
strcat(last_str, virtual_str);
}
}
break;
case 'R':
if (RetinaBase) {
if (!labels_after) {
strcat(last_str, retina_str);
}
NumberToString(Retina_AvailMem(0));
strcat(last_str, num_buf);
if (labels_after) {
strcat(last_str, retina_str);
}
}
break;
}
i++;
}
strcat(last_str, suffix_str);
old_SetWindowTitles(window, win_title, mem_str, lib);
}
else // leave other windows' titles untouched
{
old_SetWindowTitles(window, win_title, scr_title, lib);
}
}
/*
* Convert the number to a string in the format the user specified.
* The string generated is 0 terminated.
*
* Uses global variables: buff_ptr, num_buff
*
*/
static void NumberToString(ULONG number)
{
number /= units;
if (sep1000) {
buff_ptr = num_buf;
format_number(number);
*buff_ptr = 0;
} else {
stcul_d(num_buf, number);
}
}
// Gwardar: Functions below use recursive calls, so take heed when changing anything
// within 'format_number' or 'no_lead_triple' (danger of trashing unallocated memory
// when the string buffer boundary is not respected)
static void format_number(ULONG number)
{
if (number > 1000) {
format_number(number / 1000);
lead_triple(number % 1000);
} else {
no_lead_triple(number);
}
}
static void no_lead_triple(ULONG number)
{
if (number > 10) {
no_lead_triple(number / 10);
*buff_ptr++ = ((number % 10) | 0x30);
}
// Gwardar begin
else if (number == 10) {
*buff_ptr++ = (1 | 0x30); // fixes the ':' numeric display bug that v1.0
*buff_ptr++ = (0 | 0x30); // showed on some memory amounts
}
// Gwardar end
else // if (number < 10) // removed by Gwardar (smaller code, faster)
{
*buff_ptr++ = (number | 0x30);
}
}
static void lead_triple(ULONG number)
{
*buff_ptr++ = comma;
*buff_ptr++ = ((number / 100) | 0x30);
number %= 100;
*buff_ptr++ = ((number / 10) | 0x30);
number %= 10;
*buff_ptr++ = (number | 0x30);
}